Example pair

D_T8_C1R2_1 D_T8_C1R2_2 D_T8_C1R2_5-95_1_2

list_images <- read_csv("~/Desktop/Lab/emergent-coexistence/output/check/00-list_images-D.csv")
Rows: 208 Columns: 22
── Column specification ─────────────────────────────────────────────────────────────
Delimiter: ","
chr (22): image_name, folder_original, folder_green, folder_green_rolled, folder_...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

0. original image

image_original <- readImage(paste0(list_images$folder_original[i], image_name, ".tiff"))
writeImage(image_original, paste0(folder_main, "example/", image_name, "-00-original.tiff"))
display(image_original, method = "raster")

1. Green channel

temp <- image_original
colorMode(temp) = Grayscale
image_green <- temp[,,2]
writeImage(image_green, paste0(folder_main, "example/", image_name, "-01-green.tiff"))
display(image_green, method = "raster")

2. Rolling ball

Python code from rolling_ball.py

py$file_green <- paste0(list_images$folder_green[i], image_name, ".tiff")
py$file_green_rolled <- paste0(list_images$folder_green_rolled[i], image_name, "-02-rolled.tiff")

The R variables should be passed to python but somehow failed. Not sure where the problem is from, probably reticualte. 20220902 use the externally generated rolled images.

import imageio
import os
import sys
import skimage
from skimage import data, restoration, util, io, color

def rolling_ball_light(image):
    # 2. invert the image
    image_inverted = util.invert(image)
    
    # 3. rolling ball
    background_inverted = restoration.rolling_ball(image_inverted, radius = 80, num_threads = 10)
    
    # 4. invert the result
    image_rolled_inverted = image_inverted - background_inverted
    image_rolled = util.invert(image_rolled_inverted)
    return image_rolled
    
image = io.imread(file_green)
image_rolled = rolling_ball_light(image)
io.imsave(file_green_rolled, image_rolled)

Display the rolled result

image_rolled <- readImage(paste0(list_images$folder_green_rolled[i], image_name, ".tiff"))
writeImage(image_rolled, paste0(folder_main, "example/", image_name, "-02-green_rolled.tiff"))
display(image_rolled, method = "raster")

3. Thresholding

Here because the images have undergone gray scale and background subtraction so I apply a global threshold to the image

threshold <- otsu(image_rolled)
image_thresholded <- image_rolled < threshold
writeImage(image_thresholded, paste0(folder_main, "example/", image_name, "-03-threshold.tiff"))
display(image_thresholded, method = "raster")

4. Detect round shaped object and remove super small size

To select potential colonies: area, perimeter, and circularity.

This step is implemented here to prevent over segmentation and reduce segmentation load

As you can see here, attached objects are not divided yet

Filter objects by size

image_object <- bwlabel(image_thresholded)
object_ID_nonround <- detect_nonround_object(image_object, watershed = F)
image_round <- rmObjects(image_object, object_ID_nonround, reenumerate = T)
writeImage(image_round, paste0(folder_main, "example/", image_name, "-04-size_objects.tiff"))
display(image_round, method = "raster")

5. Distance map

The distance map contains for each pixel the distance to the nearest background pixe

image_distancemap <- distmap(image_round)
writeImage(normalize(image_distancemap), paste0(folder_main, "example/", image_name, "-05-distancemap.tiff"))
display(normalize(image_distancemap), method = "raster")

6. Watershed

This is the actual segmentation step. Here the adjacent objects are better distinguished

7. Features

image_watershed <- watershed(image_distancemap, tolerance = 1)
display(colorLabels(image_watershed), method = "raster")

# 7. Calculate the features
library(EBImageExtra)
library(purrr)
extract_transection <- function (watershed, ref) {
    #' This function searches for all objects on an image and return the pixel intensity along the transection of each object
    #' Arguments:
    #'  watershed is the watershed image with object labels
    #'  ref is the original image with intensity data. This has to be single-channel
    oc <- ocontour(watershed) # all contour points
    transection <- NULL
    #max_radius <- NULL # A test for object ID -> good. As long I use the oc output, they should be good
    for (j in 1:length(oc)) {
        z <- oc[[j]] # coordinate of contour pixels
        cz <- apply(z, 2, mean) # object center
        radius <- sqrt(rowSums((z - rep(cz, each=nrow(z)))^2)) # all radius from the center pixel to the contour pixels
        mz <- z[which.min(abs(radius - median(radius))),] # the coordinate of contour pixels with the median radius (or closest to the median)
        # The coordinates of all along the line to the maximum line: this is arranged such that it's always from the center to the periphery
        lz <- bresenham(c(cz[1], mz[1]), c(cz[2], mz[2]))
        radial_gradient <- NULL
        for (k in 1:length(lz$x)) radial_gradient[k] <- ref@.Data[lz$x[k], lz$y[k]]

        transection[[j]] <- tibble(x = lz$x, y = lz$y, Intensity = radial_gradient)
    }

    return(transection)
}
draw_pixels <- function (img, pixel.x, pixel.y) {
    #' This function takes an image and draw red on the assigned pixels
    stopifnot(length(pixel.x) == length(pixel.y))

    # Render the gray-scale image back into color mode
    img <- abind(img, img, img, along = 3) %>% Image(colormode = "Color")

    # Create a logical mask
    m <- Image(T, dim(img)[1:2])
    for (i in 1:length(pixel.x)) m[pixel.x[i], pixel.y[i]] <- F
    M <- abind(m, m, m, along = 3)

    # Combine with solid colored image, replace appropriate pixels in image
    mask <- Image("red", dim = dim(img)[1:2], colormode = "Color")
    ans <- img * M + mask * !M

    return(ans)
}

Plot two pairs’s transection for example

Output the example

# # writeImage(image_original, paste0(folder_main, "example/", image_name, "-00-original.tiff"))
# # writeImage(image_green, paste0(folder_main, "example/", image_name, "-01-green.tiff"))
# writeImage(image_rolled, paste0(folder_main, "example/", image_name, "-02-rolled.tiff"))
# writeImage(image_thresholded, paste0(folder_main, "example/", image_name, "-03-threshold.tiff"))
# #writeImage(colorLabels(image_object), paste0(folder_main, "example/", image_name, "-03a-object.tiff"))
# writeImage(image_round, paste0(folder_main, "example/", image_name, "-04-round_object.tiff"))
# writeImage(normalize(image_distancemap), paste0(folder_main, "example/", image_name, "-05-distance_map.tiff"))
# writeImage(colorLabels(image_watershed), paste0(folder_main, "example/", image_name, "-06-watershed.tiff"))
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiNrbml0cjo6b3B0c19jaHVuaygpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KEVCSW1hZ2UpCmxpYnJhcnkocmV0aWN1bGF0ZSkKZm9sZGVyX21haW4gPC0gIi9Vc2Vycy9jaGFuZy15dS9Ecm9wYm94L2xhYi9lbWVyZ2VudC1jb2V4aXN0ZW5jZS9kYXRhL3Jhdy9wbGF0ZV9zY2FuL2VtZXJnZW50X2NvZXhpc3RlbmNlX3BsYXRlX3NjYW5fY2hlY2svIgpgYGAKCgpFeGFtcGxlIHBhaXIgCgpEX1Q4X0MxUjJfMQpEX1Q4X0MxUjJfMiAKRF9UOF9DMVIyXzUtOTVfMV8yCgoKYGBge3J9CiMgbGlzdF9pbWFnZXMgPC0gdGliYmxlKAojICAgICBpbWFnZV9uYW1lID0gYygiRF9UOF9DMVIyXzEiLCAiRF9UOF9DMVIyXzIiLCAiRF9UOF9DMVIyXzUtOTVfMV8yIiksCiMgICAgIGZvbGRlcl9vcmlnaW5hbCA9IHJlcChwYXN0ZTAoZm9sZGVyX21haW4sICJjaGVjay9ELTAwLW9yaWdpbmFsLyIpLCAzKSwKIyAgICAgZm9sZGVyX2dyZWVuID0gcmVwKHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiksIDMpLAojICAgICBmb2xkZXJfZ3JlZW5fcm9sbGVkID0gcmVwKHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiksIDMpLAojICAgICBmb2xkZXJfZ3JlZW5fd2F0ZXJzaGVkID0gcmVwKHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiksIDMpLAojICAgICBmb2xkZXJfZ3JlZW5fZmVhdHVyZSA9IHJlcChwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIpLCAzKSwKIyAgICAgZm9sZGVyX2dyZWVuX2NsdXN0ZXIgPSByZXAocGFzdGUwKGZvbGRlcl9tYWluLCAiZXhhbXBsZS8iKSwgMykKIyApCgpsaXN0X2ltYWdlcyA8LSByZWFkX2Nzdigifi9EZXNrdG9wL0xhYi9lbWVyZ2VudC1jb2V4aXN0ZW5jZS9vdXRwdXQvY2hlY2svMDAtbGlzdF9pbWFnZXMtRC5jc3YiKQoKaSA9IHdoaWNoKGxpc3RfaW1hZ2VzJGltYWdlX25hbWUgPT0gIkRfVDhfQzFSMl8xIikKaW1hZ2VfbmFtZSA8LSBsaXN0X2ltYWdlcyRpbWFnZV9uYW1lW2ldCgpgYGAKCiMgMC4gb3JpZ2luYWwgaW1hZ2UKCmBgYHtyfQppbWFnZV9vcmlnaW5hbCA8LSByZWFkSW1hZ2UocGFzdGUwKGxpc3RfaW1hZ2VzJGZvbGRlcl9vcmlnaW5hbFtpXSwgaW1hZ2VfbmFtZSwgIi50aWZmIikpCndyaXRlSW1hZ2UoaW1hZ2Vfb3JpZ2luYWwsIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wMC1vcmlnaW5hbC50aWZmIikpCmRpc3BsYXkoaW1hZ2Vfb3JpZ2luYWwsIG1ldGhvZCA9ICJyYXN0ZXIiKQpgYGAKCiMgMS4gR3JlZW4gY2hhbm5lbAoKYGBge3J9CnRlbXAgPC0gaW1hZ2Vfb3JpZ2luYWwKY29sb3JNb2RlKHRlbXApID0gR3JheXNjYWxlCmltYWdlX2dyZWVuIDwtIHRlbXBbLCwyXQp3cml0ZUltYWdlKGltYWdlX2dyZWVuLCBwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIsIGltYWdlX25hbWUsICItMDEtZ3JlZW4udGlmZiIpKQpkaXNwbGF5KGltYWdlX2dyZWVuLCBtZXRob2QgPSAicmFzdGVyIikKYGBgCgoKCiMgMi4gUm9sbGluZyBiYWxsCgpQeXRob24gY29kZSBmcm9tIGByb2xsaW5nX2JhbGwucHlgCgpgYGB7cn0KcHkkZmlsZV9ncmVlbiA8LSBwYXN0ZTAobGlzdF9pbWFnZXMkZm9sZGVyX2dyZWVuW2ldLCBpbWFnZV9uYW1lLCAiLnRpZmYiKQpweSRmaWxlX2dyZWVuX3JvbGxlZCA8LSBwYXN0ZTAobGlzdF9pbWFnZXMkZm9sZGVyX2dyZWVuX3JvbGxlZFtpXSwgaW1hZ2VfbmFtZSwgIi0wMi1yb2xsZWQudGlmZiIpCmBgYAoKVGhlIFIgdmFyaWFibGVzIHNob3VsZCBiZSBwYXNzZWQgdG8gcHl0aG9uIGJ1dCBzb21laG93IGZhaWxlZC4gTm90IHN1cmUgd2hlcmUgdGhlIHByb2JsZW0gaXMgZnJvbSwgcHJvYmFibHkgcmV0aWN1YWx0ZS4gMjAyMjA5MDIgdXNlIHRoZSBleHRlcm5hbGx5IGdlbmVyYXRlZCByb2xsZWQgaW1hZ2VzLgoKYGBge3B5dGhvbiBldmFsID0gRn0KaW1wb3J0IGltYWdlaW8KaW1wb3J0IG9zCmltcG9ydCBzeXMKaW1wb3J0IHNraW1hZ2UKZnJvbSBza2ltYWdlIGltcG9ydCBkYXRhLCByZXN0b3JhdGlvbiwgdXRpbCwgaW8sIGNvbG9yCgpkZWYgcm9sbGluZ19iYWxsX2xpZ2h0KGltYWdlKToKICAgICMgMi4gaW52ZXJ0IHRoZSBpbWFnZQogICAgaW1hZ2VfaW52ZXJ0ZWQgPSB1dGlsLmludmVydChpbWFnZSkKICAgIAogICAgIyAzLiByb2xsaW5nIGJhbGwKICAgIGJhY2tncm91bmRfaW52ZXJ0ZWQgPSByZXN0b3JhdGlvbi5yb2xsaW5nX2JhbGwoaW1hZ2VfaW52ZXJ0ZWQsIHJhZGl1cyA9IDgwLCBudW1fdGhyZWFkcyA9IDEwKQogICAgCiAgICAjIDQuIGludmVydCB0aGUgcmVzdWx0CiAgICBpbWFnZV9yb2xsZWRfaW52ZXJ0ZWQgPSBpbWFnZV9pbnZlcnRlZCAtIGJhY2tncm91bmRfaW52ZXJ0ZWQKICAgIGltYWdlX3JvbGxlZCA9IHV0aWwuaW52ZXJ0KGltYWdlX3JvbGxlZF9pbnZlcnRlZCkKICAgIHJldHVybiBpbWFnZV9yb2xsZWQKICAgIAppbWFnZSA9IGlvLmltcmVhZChmaWxlX2dyZWVuKQppbWFnZV9yb2xsZWQgPSByb2xsaW5nX2JhbGxfbGlnaHQoaW1hZ2UpCmlvLmltc2F2ZShmaWxlX2dyZWVuX3JvbGxlZCwgaW1hZ2Vfcm9sbGVkKQpgYGAKCkRpc3BsYXkgdGhlIHJvbGxlZCByZXN1bHQKCmBgYHtyfQppbWFnZV9yb2xsZWQgPC0gcmVhZEltYWdlKHBhc3RlMChsaXN0X2ltYWdlcyRmb2xkZXJfZ3JlZW5fcm9sbGVkW2ldLCBpbWFnZV9uYW1lLCAiLnRpZmYiKSkKd3JpdGVJbWFnZShpbWFnZV9yb2xsZWQsIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wMi1ncmVlbl9yb2xsZWQudGlmZiIpKQpkaXNwbGF5KGltYWdlX3JvbGxlZCwgbWV0aG9kID0gInJhc3RlciIpCmBgYAoKCgojIDMuIFRocmVzaG9sZGluZwoKSGVyZSBiZWNhdXNlIHRoZSBpbWFnZXMgaGF2ZSB1bmRlcmdvbmUgZ3JheSBzY2FsZSBhbmQgYmFja2dyb3VuZCBzdWJ0cmFjdGlvbiBzbyBJIGFwcGx5IGEgZ2xvYmFsIHRocmVzaG9sZCB0byB0aGUgaW1hZ2UKCmBgYHtyfQp0aHJlc2hvbGQgPC0gb3RzdShpbWFnZV9yb2xsZWQpCmltYWdlX3RocmVzaG9sZGVkIDwtIGltYWdlX3JvbGxlZCA8IHRocmVzaG9sZAp3cml0ZUltYWdlKGltYWdlX3RocmVzaG9sZGVkLCBwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIsIGltYWdlX25hbWUsICItMDMtdGhyZXNob2xkLnRpZmYiKSkKZGlzcGxheShpbWFnZV90aHJlc2hvbGRlZCwgbWV0aG9kID0gInJhc3RlciIpCmBgYAoKIyA0LiBEZXRlY3Qgcm91bmQgc2hhcGVkIG9iamVjdCBhbmQgcmVtb3ZlIHN1cGVyIHNtYWxsIHNpemUKClRvIHNlbGVjdCBwb3RlbnRpYWwgY29sb25pZXM6IGFyZWEsIHBlcmltZXRlciwgYW5kIGNpcmN1bGFyaXR5LgoKVGhpcyBzdGVwIGlzIGltcGxlbWVudGVkIGhlcmUgdG8gcHJldmVudCBvdmVyIHNlZ21lbnRhdGlvbiBhbmQgcmVkdWNlIHNlZ21lbnRhdGlvbiBsb2FkCgpBcyB5b3UgY2FuIHNlZSBoZXJlLCBhdHRhY2hlZCBvYmplY3RzIGFyZSBub3QgZGl2aWRlZCB5ZXQKCmBgYHtyfQpkZXRlY3Rfbm9ucm91bmRfb2JqZWN0IDwtIGZ1bmN0aW9uIChpbWFnZV9vYmplY3QsIHdhdGVyc2hlZCA9IEYpIHsKICAgICMgUmVvbXZlIHRvbyBsYXJnZSBvciB0b28gc21hbGwgb2JqZWN0cyBiZWZvcmUgd2F0ZXJzaGVkIHRvIHJlZHVjZSBjb21wdXRhdGlvbmFsIGxvYWQKICAgIGlmICghd2F0ZXJzaGVkKSB7CiAgICAgICAgb2JqZWN0X3NoYXBlIDwtIGNvbXB1dGVGZWF0dXJlcy5zaGFwZShpbWFnZV9vYmplY3QpICU+JSBhc190aWJibGUocm93bmFtZXMgPSAiT2JqZWN0SUQiKQogICAgICAgIG9iamVjdF9zaGFwZV9yb3VuZCA8LSBvYmplY3Rfc2hhcGUgJT4lCiAgICAgICAgICAgICMgQXJlYQogICAgICAgICAgICBmaWx0ZXIocy5hcmVhID4gMzAwICYgcy5hcmVhIDwgMjAwMDApCiAgICB9CgogICAgIyBGaWx0ZXIgZm9yIGNpcmN1bGFyaXR5IG9ubHkgYWZ0ZXIgd2F0ZXJzaGVkIHNlZ21lbnRhdGlvbgogICAgaWYgKHdhdGVyc2hlZCkgewogICAgICAgIG9iamVjdF9zaGFwZSA8LSBjb21wdXRlRmVhdHVyZXMuc2hhcGUoaW1hZ2Vfb2JqZWN0KSAlPiUgYXNfdGliYmxlKHJvd25hbWVzID0gIk9iamVjdElEIikKICAgICAgICBvYmplY3RfbW9tZW50IDwtIGNvbXB1dGVGZWF0dXJlcy5tb21lbnQoaW1hZ2Vfb2JqZWN0KSAlPiUgYXNfdGliYmxlKHJvd25hbWVzID0gIk9iamVjdElEIikKCiAgICAgICAgb2JqZWN0X3NoYXBlX3JvdW5kIDwtIG9iamVjdF9zaGFwZSAlPiUKICAgICAgICAgICAgbGVmdF9qb2luKG9iamVjdF9tb21lbnQsIGJ5ID0gIk9iamVjdElEIikgJT4lCiAgICAgICAgICAgICMgQ2lyY3VsYXJpdHkgPSAxIG1lYW5zIGEgcGVyZmVjdCBjaXJjbGUgYW5kIGdvZXMgZG93biB0byAwIGZvciBub24tY2lyY3VsYXIgc2hhcGVzCiAgICAgICAgICAgIG11dGF0ZShDaXJjdWxhcml0eSA9IDQgKiBwaSAqIHMuYXJlYSAvIHMucGVyaW1ldGVyXjIpICU+JQogICAgICAgICAgICBmaWx0ZXIoQ2lyY3VsYXJpdHkgPiAwLjMpICU+JQogICAgICAgICAgICAjIFJlbW92ZSB0YXBlIGFuZCBsYWJlbCB0aGF0IGhhcyByZWFsbHkgbGFyZ2UgdmFyaWF0aW9uIGluIHJhZGl1cwogICAgICAgICAgICBmaWx0ZXIocy5yYWRpdXMuc2Qvcy5yYWRpdXMubWVhbiA8IDEvMikgJT4lCiAgICAgICAgICAgIGZpbHRlcihtLmVjY2VudHJpY2l0eSA8IDAuOCkgIyBDaXJjbGUgZWNjZW50cmljaXR5PTAsIHN0cmFpZ2h0IGxpbmUgZWNjZW50cmljaXR5PTEKICAgIH0KCiAgICAjIEFycmFuZ2UgYnkgYXJlYSBzaXplCiAgICBvYmplY3Rfc2hhcGVfcm91bmQgPC0gb2JqZWN0X3NoYXBlX3JvdW5kICU+JSBhcnJhbmdlKGRlc2Mocy5hcmVhKSkKICAgIG9iamVjdF9JRF9ub25yb3VuZCA8LSBvYmplY3Rfc2hhcGUkT2JqZWN0SURbIShvYmplY3Rfc2hhcGUkT2JqZWN0SUQgJWluJSBvYmplY3Rfc2hhcGVfcm91bmQkT2JqZWN0SUQpXQogICAgcmV0dXJuKG9iamVjdF9JRF9ub25yb3VuZCkKfQpgYGAKCgpGaWx0ZXIgb2JqZWN0cyBieSBzaXplCgpgYGB7cn0KaW1hZ2Vfb2JqZWN0IDwtIGJ3bGFiZWwoaW1hZ2VfdGhyZXNob2xkZWQpCm9iamVjdF9JRF9ub25yb3VuZCA8LSBkZXRlY3Rfbm9ucm91bmRfb2JqZWN0KGltYWdlX29iamVjdCwgd2F0ZXJzaGVkID0gRikKaW1hZ2Vfcm91bmQgPC0gcm1PYmplY3RzKGltYWdlX29iamVjdCwgb2JqZWN0X0lEX25vbnJvdW5kLCByZWVudW1lcmF0ZSA9IFQpCndyaXRlSW1hZ2UoaW1hZ2Vfcm91bmQsIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wNC1zaXplX29iamVjdHMudGlmZiIpKQpkaXNwbGF5KGltYWdlX3JvdW5kLCBtZXRob2QgPSAicmFzdGVyIikKYGBgCgoKIyA1LiBEaXN0YW5jZSBtYXAKClRoZSBkaXN0YW5jZSBtYXAgY29udGFpbnMgZm9yIGVhY2ggcGl4ZWwgdGhlIGRpc3RhbmNlIHRvIHRoZSBuZWFyZXN0IGJhY2tncm91bmQgcGl4ZQoKCmBgYHtyfQppbWFnZV9kaXN0YW5jZW1hcCA8LSBkaXN0bWFwKGltYWdlX3JvdW5kKQp3cml0ZUltYWdlKG5vcm1hbGl6ZShpbWFnZV9kaXN0YW5jZW1hcCksIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wNS1kaXN0YW5jZW1hcC50aWZmIikpCmRpc3BsYXkobm9ybWFsaXplKGltYWdlX2Rpc3RhbmNlbWFwKSwgbWV0aG9kID0gInJhc3RlciIpCmBgYAoKCiMgNi4gV2F0ZXJzaGVkCgpUaGlzIGlzIHRoZSBhY3R1YWwgc2VnbWVudGF0aW9uIHN0ZXAuIEhlcmUgdGhlIGFkamFjZW50IG9iamVjdHMgYXJlIGJldHRlciBkaXN0aW5ndWlzaGVkCgpgYGB7cn0KaW1hZ2Vfd2F0ZXJzaGVkIDwtIHdhdGVyc2hlZChpbWFnZV9kaXN0YW5jZW1hcCwgdG9sZXJhbmNlID0gMSkKIyBBIHNlY29uZCBmaWx0ZXIgYnkgZGVuc2l0eSBhbmQgZWNjZW50cmljaXR5Cm9iamVjdF9JRF9ub25yb3VuZDIgPC0gZGV0ZWN0X25vbnJvdW5kX29iamVjdChpbWFnZV93YXRlcnNoZWQsIHdhdGVyc2hlZCA9IFQpCmltYWdlX3dhdGVyc2hlZDIgPC0gcm1PYmplY3RzKGltYWdlX3dhdGVyc2hlZCwgb2JqZWN0X0lEX25vbnJvdW5kMiwgcmVlbnVtZXJhdGUgPSBUKQp3cml0ZUltYWdlKGNvbG9yTGFiZWxzKGltYWdlX3dhdGVyc2hlZDIpLCBwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIsIGltYWdlX25hbWUsICItMDYtd2F0ZXJzaGVkLnRpZmYiKSkKZGlzcGxheShjb2xvckxhYmVscyhpbWFnZV93YXRlcnNoZWQyKSwgbWV0aG9kID0gInJhc3RlciIpCmBgYAojIDcuIEZlYXR1cmVzCgpgYGB7cn0KaW1hZ2Vfd2F0ZXJzaGVkIDwtIHdhdGVyc2hlZChpbWFnZV9kaXN0YW5jZW1hcCwgdG9sZXJhbmNlID0gMSkKZGlzcGxheShjb2xvckxhYmVscyhpbWFnZV93YXRlcnNoZWQpLCBtZXRob2QgPSAicmFzdGVyIikKYGBgCgoKIyMKCmBgYHtyfQojIDcuIENhbGN1bGF0ZSB0aGUgZmVhdHVyZXMKbGlicmFyeShFQkltYWdlRXh0cmEpCmxpYnJhcnkocHVycnIpCmV4dHJhY3RfdHJhbnNlY3Rpb24gPC0gZnVuY3Rpb24gKHdhdGVyc2hlZCwgcmVmKSB7CiAgICAjJyBUaGlzIGZ1bmN0aW9uIHNlYXJjaGVzIGZvciBhbGwgb2JqZWN0cyBvbiBhbiBpbWFnZSBhbmQgcmV0dXJuIHRoZSBwaXhlbCBpbnRlbnNpdHkgYWxvbmcgdGhlIHRyYW5zZWN0aW9uIG9mIGVhY2ggb2JqZWN0CiAgICAjJyBBcmd1bWVudHM6CiAgICAjJyAgd2F0ZXJzaGVkIGlzIHRoZSB3YXRlcnNoZWQgaW1hZ2Ugd2l0aCBvYmplY3QgbGFiZWxzCiAgICAjJyAgcmVmIGlzIHRoZSBvcmlnaW5hbCBpbWFnZSB3aXRoIGludGVuc2l0eSBkYXRhLiBUaGlzIGhhcyB0byBiZSBzaW5nbGUtY2hhbm5lbAogICAgb2MgPC0gb2NvbnRvdXIod2F0ZXJzaGVkKSAjIGFsbCBjb250b3VyIHBvaW50cwogICAgdHJhbnNlY3Rpb24gPC0gTlVMTAogICAgI21heF9yYWRpdXMgPC0gTlVMTCAjIEEgdGVzdCBmb3Igb2JqZWN0IElEIC0+IGdvb2QuIEFzIGxvbmcgSSB1c2UgdGhlIG9jIG91dHB1dCwgdGhleSBzaG91bGQgYmUgZ29vZAogICAgZm9yIChqIGluIDE6bGVuZ3RoKG9jKSkgewogICAgICAgIHogPC0gb2NbW2pdXSAjIGNvb3JkaW5hdGUgb2YgY29udG91ciBwaXhlbHMKICAgICAgICBjeiA8LSBhcHBseSh6LCAyLCBtZWFuKSAjIG9iamVjdCBjZW50ZXIKICAgICAgICByYWRpdXMgPC0gc3FydChyb3dTdW1zKCh6IC0gcmVwKGN6LCBlYWNoPW5yb3coeikpKV4yKSkgIyBhbGwgcmFkaXVzIGZyb20gdGhlIGNlbnRlciBwaXhlbCB0byB0aGUgY29udG91ciBwaXhlbHMKICAgICAgICBteiA8LSB6W3doaWNoLm1pbihhYnMocmFkaXVzIC0gbWVkaWFuKHJhZGl1cykpKSxdICMgdGhlIGNvb3JkaW5hdGUgb2YgY29udG91ciBwaXhlbHMgd2l0aCB0aGUgbWVkaWFuIHJhZGl1cyAob3IgY2xvc2VzdCB0byB0aGUgbWVkaWFuKQogICAgICAgICMgVGhlIGNvb3JkaW5hdGVzIG9mIGFsbCBhbG9uZyB0aGUgbGluZSB0byB0aGUgbWF4aW11bSBsaW5lOiB0aGlzIGlzIGFycmFuZ2VkIHN1Y2ggdGhhdCBpdCdzIGFsd2F5cyBmcm9tIHRoZSBjZW50ZXIgdG8gdGhlIHBlcmlwaGVyeQogICAgICAgIGx6IDwtIGJyZXNlbmhhbShjKGN6WzFdLCBtelsxXSksIGMoY3pbMl0sIG16WzJdKSkKICAgICAgICByYWRpYWxfZ3JhZGllbnQgPC0gTlVMTAogICAgICAgIGZvciAoayBpbiAxOmxlbmd0aChseiR4KSkgcmFkaWFsX2dyYWRpZW50W2tdIDwtIHJlZkAuRGF0YVtseiR4W2tdLCBseiR5W2tdXQoKICAgICAgICB0cmFuc2VjdGlvbltbal1dIDwtIHRpYmJsZSh4ID0gbHokeCwgeSA9IGx6JHksIEludGVuc2l0eSA9IHJhZGlhbF9ncmFkaWVudCkKICAgIH0KCiAgICByZXR1cm4odHJhbnNlY3Rpb24pCn0KZHJhd19waXhlbHMgPC0gZnVuY3Rpb24gKGltZywgcGl4ZWwueCwgcGl4ZWwueSkgewogICAgIycgVGhpcyBmdW5jdGlvbiB0YWtlcyBhbiBpbWFnZSBhbmQgZHJhdyByZWQgb24gdGhlIGFzc2lnbmVkIHBpeGVscwogICAgc3RvcGlmbm90KGxlbmd0aChwaXhlbC54KSA9PSBsZW5ndGgocGl4ZWwueSkpCgogICAgIyBSZW5kZXIgdGhlIGdyYXktc2NhbGUgaW1hZ2UgYmFjayBpbnRvIGNvbG9yIG1vZGUKICAgIGltZyA8LSBhYmluZChpbWcsIGltZywgaW1nLCBhbG9uZyA9IDMpICU+JSBJbWFnZShjb2xvcm1vZGUgPSAiQ29sb3IiKQoKICAgICMgQ3JlYXRlIGEgbG9naWNhbCBtYXNrCiAgICBtIDwtIEltYWdlKFQsIGRpbShpbWcpWzE6Ml0pCiAgICBmb3IgKGkgaW4gMTpsZW5ndGgocGl4ZWwueCkpIG1bcGl4ZWwueFtpXSwgcGl4ZWwueVtpXV0gPC0gRgogICAgTSA8LSBhYmluZChtLCBtLCBtLCBhbG9uZyA9IDMpCgogICAgIyBDb21iaW5lIHdpdGggc29saWQgY29sb3JlZCBpbWFnZSwgcmVwbGFjZSBhcHByb3ByaWF0ZSBwaXhlbHMgaW4gaW1hZ2UKICAgIG1hc2sgPC0gSW1hZ2UoInJlZCIsIGRpbSA9IGRpbShpbWcpWzE6Ml0sIGNvbG9ybW9kZSA9ICJDb2xvciIpCiAgICBhbnMgPC0gaW1nICogTSArIG1hc2sgKiAhTQoKICAgIHJldHVybihhbnMpCn0KYGBgCgoKCmBgYHtyfQppbWFnZV9uYW1lIDwtIGxpc3RfaW1hZ2VzJGltYWdlX25hbWVbaV0KaW1hZ2Vfcm9sbGVkIDwtIHJlYWRJbWFnZShwYXN0ZTAobGlzdF9pbWFnZXMkZm9sZGVyX2dyZWVuX3JvbGxlZFtpXSwgaW1hZ2VfbmFtZSwgIi50aWZmIikpCmxvYWQocGFzdGUwKGxpc3RfaW1hZ2VzJGZvbGRlcl9ncmVlbl93YXRlcnNoZWRfZmlsZVtpXSwgaW1hZ2VfbmFtZSwgIi5SRGF0YSIpKSAjIHRoaXMgc2hvdWxkIGNvbnRhaW4gb25lIFIgb2JqZWN0IGltYWdlX3dhdGVyc2hlZDIKCnRyYW5zZWN0aW9uIDwtIGV4dHJhY3RfdHJhbnNlY3Rpb24oaW1hZ2Vfd2F0ZXJzaGVkMiwgaW1hZ2Vfcm9sbGVkKSAlPiUKICAgIGxhcHBseShmdW5jdGlvbih4KSBtdXRhdGUoeCwgRGlzdGFuY2VUb0NlbnRlciA9IDE6bGVuZ3RoKHgpKSkgJT4lCiAgICBiaW5kX3Jvd3MoLmlkID0gIk9iamVjdElEIikKCiMgU2F2ZSB0aGUgdHJhbnNlY3Rpb24gaW1hZ2UKaW1hZ2VfdHJhbnNlY3QgPC0gZHJhd19waXhlbHMoaW1hZ2Vfcm9sbGVkLCB0cmFuc2VjdGlvbiR4LCB0cmFuc2VjdGlvbiR5KQp3cml0ZUltYWdlKGltYWdlX3RyYW5zZWN0LCBwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIsIGltYWdlX25hbWUsICItMDctdHJhbnNlY3Rpb24udGlmZiIpKQpkaXNwbGF5KGltYWdlX3RyYW5zZWN0LCBtZXRob2QgPSAicmFzdGVyIikKCmBgYAoKClBsb3QgdHdvIHBhaXJzJ3MgdHJhbnNlY3Rpb24gZm9yIGV4YW1wbGUKCmBgYHtyfQoicmVhZCB0d28gdHJhbnNlY3Rpb24gZGF0YSAoaW4gdGFuc2VjdCBmb2xkZXIgYW5kIGNzdikgYW5kIHBsb3QgaXQgIgoKcmVhZF9jc3YocGFzdGUwKGZvbGRlcl9tYWluLCApKQoKIyB0cmFuc2VjdGlvbnMKIwojIGkgPSA1CiMgdHJhbnNlY3Rpb24gPC0gdHJhbnNlY3Rpb25zW1tpXV0KIwojICMgcGxvdCB0d28gaXNvbGF0ZXMKIwojIGJpbmRfcm93cyhtdXRhdGUodHJhbnNlY3Rpb25zW1s0XV0sIEdyb3VwID0gImlzb2xhdGUxIiksCiMgICAgICAgICAgIG11dGF0ZSh0cmFuc2VjdGlvbnNbWzVdXSwgR3JvdXAgPSAiaXNvbGF0ZTIiKSwKIyAgICAgICAgICAgbXV0YXRlKHRyYW5zZWN0aW9uc1tbNl1dLCBHcm91cCA9ICJwYWlyIikKIyApICU+JQojICAgICBnZ3Bsb3QoKSArCiMgICAgIGdlb21fbGluZShhZXMoeCA9IERpc3RhbmNlVG9DZW50ZXIsIHkgPSBJbnRlbnNpdHksIGdyb3VwID0gaW50ZXJhY3Rpb24oR3JvdXAsIE9iamVjdElEKSwgY29sb3IgPSBHcm91cCksIGx3ZCA9IC4zKSArCiMgICAgICNnZW9tX3BvaW50KGRhdGEgPSB0cmFuc2VjdGlvbnNbWzFdXSwgYWVzKHggPSBEaXN0YW5jZVRvQ2VudGVyLCB5ID0gSW50ZW5zaXR5LCBncm91cCA9IE9iamVjdElEKSkgKwojICAgICB0aGVtZV9jbGFzc2ljKCkgKwojICAgICAjZ3VpZGVzKGNvbG9yID0gIm5vbmUiKSArCiMgICAgIGxhYnMoKQoKCgpgYGAKCgoKIyBPdXRwdXQgdGhlIGV4YW1wbGUKCmBgYHtyfQojICMgd3JpdGVJbWFnZShpbWFnZV9vcmlnaW5hbCwgcGFzdGUwKGZvbGRlcl9tYWluLCAiZXhhbXBsZS8iLCBpbWFnZV9uYW1lLCAiLTAwLW9yaWdpbmFsLnRpZmYiKSkKIyAjIHdyaXRlSW1hZ2UoaW1hZ2VfZ3JlZW4sIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wMS1ncmVlbi50aWZmIikpCiMgd3JpdGVJbWFnZShpbWFnZV9yb2xsZWQsIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wMi1yb2xsZWQudGlmZiIpKQojIHdyaXRlSW1hZ2UoaW1hZ2VfdGhyZXNob2xkZWQsIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wMy10aHJlc2hvbGQudGlmZiIpKQojICN3cml0ZUltYWdlKGNvbG9yTGFiZWxzKGltYWdlX29iamVjdCksIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wM2Etb2JqZWN0LnRpZmYiKSkKIyB3cml0ZUltYWdlKGltYWdlX3JvdW5kLCBwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIsIGltYWdlX25hbWUsICItMDQtcm91bmRfb2JqZWN0LnRpZmYiKSkKIyB3cml0ZUltYWdlKG5vcm1hbGl6ZShpbWFnZV9kaXN0YW5jZW1hcCksIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wNS1kaXN0YW5jZV9tYXAudGlmZiIpKQojIHdyaXRlSW1hZ2UoY29sb3JMYWJlbHMoaW1hZ2Vfd2F0ZXJzaGVkKSwgcGFzdGUwKGZvbGRlcl9tYWluLCAiZXhhbXBsZS8iLCBpbWFnZV9uYW1lLCAiLTA2LXdhdGVyc2hlZC50aWZmIikpCmBgYAoKCgoKCgoKCgoKCgo=